package com.linln.admin.system.service.impl;

import com.google.common.collect.ImmutableMap;
import com.linln.admin.system.domain.Alarm;
import com.linln.admin.system.domain.Schedule;
import com.linln.admin.system.repository.AlarmRepository;
import com.linln.admin.system.service.DistributeLockComponent;
import com.linln.admin.system.service.ScheduleService;
import com.linln.admin.system.service.ql.CommonUtilMethodRuleService;
import com.linln.admin.system.service.ql.RuleComponent;
import com.linln.common.constant.StatusConst;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Date;

@Slf4j
@Component
public class ScheduleJobDetail extends QuartzJobBean {
    public static final String MONITOR_OTHER = "0";
    @Autowired
    private DistributeLockComponent lockComponent;
    @Resource
    private RuleComponent ruleComponent;
    @Resource
    private ScheduleService scheduleService;
    @Resource
    private AlarmRepository alarmRepository;
    @Resource
    private CommonUtilMethodRuleService commonRuleService;


    /**
     * 执行定时计划
     *
     * @param context
     * @throws JobExecutionException
     */
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        //注意执行时的排他性，防止分布式部署
        String key = context.getTrigger().getJobKey().getName() + "@" + context.getTrigger().getJobKey().getGroup();
        if (!lockComponent.tryLock(key, 300)) {
            return;//已经在执行了，就不要操作了。
        }
        try {
            Schedule schedule = (Schedule) context.getMergedJobDataMap().get(ScheduleServiceImpl.JOB_ENTITY_KEY);
            Schedule db = scheduleService.getById(schedule.getId());
            if (db == null || db.getStatus().byteValue() != StatusConst.OK) {
                return;
            }
            ruleComponent.execute(ImmutableMap.of("schedule", db), db.getExeccode(), 240_000);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage(), e);
        } finally {
            lockComponent.releaseLock(key);
        }
    }

    /**
     * 发送预警信息
     *
     * @param checker       是否达到发送的条件
     * @param servType      应用类型：dubbo或http应用等
     * @param servId        服务应用记录主键id
     * @param dimension     监控预警的维度
     * @param alarmInterval 预警间隔，单位：分钟
     * @param alarmMsg      预警信息
     * @param resumeMsg     预警恢复后的提示信息
     */
    public void sendAlarm(Checker checker, int servType, Long servId, int dimension, Long alarmInterval, String alarmMsg, String resumeMsg, String wechatToken) {
        //TDOO 加并发锁
        String key = "sendAlarm_" + servType + "#" + servId + "#" + dimension;
        if (!lockComponent.tryLock(key, 60)) {
            return;//如果已经有在操作的，不用处理
        }
        try {
            Alarm alarm = alarmRepository.findByServTypeAndId(servType, servId, dimension);
            alarmMsg = StringUtils.isBlank(alarmMsg) ? "" : ("<font color=\"warning\">【告警信息】</font>" + alarmMsg);
            resumeMsg = StringUtils.isBlank(resumeMsg) ? "" : ("<font color=\"info\">【告警恢复】</font>" + resumeMsg);
            if (checker.check()) {//需要预警
                if (alarm == null) {
                    //保存并预警
                    alarm = new Alarm();
                    alarm.setAlarm_time(new Date());
                    alarm.setContent(alarmMsg);
                    alarm.setDimension(dimension);
                    alarm.setStatus(StatusConst.OK);
                    alarm.setSev_data_id(servId);
                    alarm.setSrv_type(servType);
                    alarmRepository.save(alarm);
                    commonRuleService.sendNotice(alarmMsg, wechatToken);
                } else {
                    alarmInterval = alarmInterval == null ? 30 : alarmInterval;
                    //查看距离上一次预警的内容是否一样，如果一样且时间未到，则不需要预警，否则预警并更新预警时间
                    if (StringUtils.equals(alarmMsg, alarm.getContent()) && (System.currentTimeMillis() - alarm.getAlarm_time().getTime() < alarmInterval * 60_000)) {
                        return;//不做任何处理
                    }
                    alarm.setAlarm_time(new Date());
                    alarm.setContent(alarmMsg);
                    alarm.setStatus(StatusConst.OK);
                    alarmRepository.saveFully(alarm);
                    commonRuleService.sendNotice(alarmMsg, wechatToken);
                }
            } else {
                if (alarm != null) {
                    //存在预警信息，则删除，并发送恢复预警的消息
                    alarmRepository.delete(alarm);
                    commonRuleService.sendNotice(resumeMsg, wechatToken);
                }
            }
        } finally {
            lockComponent.releaseLock(key);
        }
    }

    /**
     * 是否需要预警的判断
     */
    public interface Checker {
        /**
         * 是否需要预警
         *
         * @return true-需要；false-不需要
         */
        boolean check();
    }

    /**
     * 监控维度
     */
    public enum MonitorDimension {
        OTHER(0, "其它"), NODE_ALIVE(1, "服务存活"), JVM_THREADS(2, "JVM线程数"), JVM_MEMORY(3, "JVM内存"), CPU_RATIO(4, "CPU使用率");
        private int type;
        private String title;

        MonitorDimension(int type, String title) {
            this.type = type;
            this.title = title;
        }

        public int getType() {
            return type;
        }

        public void setType(int type) {
            this.type = type;
        }

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }
    }
}
